﻿using Orleans;
using Orleans.Runtime;
using Orleans.Storage;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace iTool.ClusterComponent
{
    public interface IZSetReader : iToolServiceWithStringKey
    {
        Task SetAsync(string payload, long score);
        Task<SortedList<long, KeyValuePair<int, string>>> GetAsync();
        Task<string> GetByScoreAsync(long score);
        Task<string> GetByIndexAsync(int index);
        Task<ICollection<string>> GetRangeAsync(long startScore,long endScore);
        Task RemoveByIndexAsync(int index);
        Task RemoveByScoreAsync(long score);
        Task RemoveRangeAsync(long startScore, long endScore);
        Task RemoveAsync(string payload);
        Task RemoveAsync();
    }


    public class ZSetReader : iToolServiceBase<ZSetState, ZSetExcuteBase>, IZSetReader
    {
        public ZSetReader() : base(100) { }
        public Task SetAsync(string payload, long score)
        {
            this._SetAsync(payload, score);
            this.RaiseEvent(new ZSet { Payload = payload, Score = score });
            return Task.CompletedTask;
        }

        public void _SetAsync(string bytes, long score)
        {
            this.State.keyValuePairs = this.State.keyValuePairs ?? new SortedList<long, KeyValuePair<int, string>>();
            if (this.State.keyValuePairs.ContainsKey(score))
            {
                var sorte = this.State.keyValuePairs[score];
                this.State.keyValuePairs[score] = new KeyValuePair<int, string>(sorte.Key, bytes);
            }
            else
            {
                this.State.keyValuePairs.Add(score, new KeyValuePair<int, string>(0, bytes));
            }
        }

        public Task<SortedList<long, KeyValuePair<int, string>>> GetAsync()
        {
            return Task.FromResult(this.State.keyValuePairs);
        }

        public Task<string> GetByScoreAsync(long score)
        {
            if (this.State.keyValuePairs?.ContainsKey(score) == true)
            {
                return Task.FromResult(this.State.keyValuePairs[score].Value);
            }
            return Task.FromResult(string.Empty);
        }

        public Task<string> GetByIndexAsync(int index)
        {
            return Task.FromResult(this.State.keyValuePairs?.ElementAtOrDefault(index).Value.Value);
        }

        public Task<ICollection<string>> GetRangeAsync(long startScore, long endScore)
        {
            bool isStartScore = startScore > 0;
            bool isEndScore = endScore > 0;

            ICollection<string> result = new List<string>();

            if (startScore < 0 & endScore < 0)
            {
                return Task.FromResult(result);
            }

            foreach (var item in this.State.keyValuePairs)
            {
                if (isEndScore && isStartScore)
                {
                    if (item.Value.Key <= endScore && item.Value.Key >= startScore)
                    {
                        result.Add(item.Value.Value);
                    }
                }
                else if (isStartScore)
                {
                    if (item.Value.Key >= startScore)
                    {
                        result.Add(item.Value.Value);
                    }
                }
                else if (isEndScore)
                {
                    result.Add(item.Value.Value);
                }
            }
            return Task.FromResult(result);
        }


        public Task RemoveByIndexAsync(int index)
        {
            this._RemoveByIndexAsync(index);
            this.RaiseEvent(new ZSetRemoveIndex { Index = index });
            return Task.CompletedTask;
        }

        public Task RemoveByScoreAsync(long score)
        {
            this._RemoveByScoreAsync(score);
            this.RaiseEvent(new ZSetRemoveScore { Score = score });
            return Task.CompletedTask;
        }

        public Task RemoveRangeAsync(long startScore, long endScore)
        {
            this._RemoveRangeAsync(startScore, endScore);
            this.RaiseEvent(new ZSetRemoveRange { StartScore = startScore, EndScore = endScore });
            return Task.CompletedTask;
        }

        public Task RemoveAsync(string payload)
        {
            this._RemoveAsync(payload);
            this.RaiseEvent(new ZSetRemoveBytes { Payload = payload });
            return Task.CompletedTask;
        }

        public void _RemoveByIndexAsync(int index)
        {
            if (this.State.keyValuePairs != null)
            {
                var element = this.State.keyValuePairs.ElementAt(index);
                if (element.Value.Key > 0)
                {
                    this.State.keyValuePairs.RemoveAt(index);
                }
            }

        }

        public void _RemoveByScoreAsync(long score)
        {
            if (this.State.keyValuePairs?.ContainsKey(score) == true)
            {
                this.State.keyValuePairs.Remove(score);
            }
        }

        public void _RemoveRangeAsync(long startScore, long endScore)
        {
            bool isStartScore = startScore > 0;
            bool isEndScore = endScore > 0;
            ICollection<int> result = new List<int>();
            foreach (var item in this.State?.keyValuePairs)
            {
                if (isEndScore)
                {
                    if (item.Value.Key <= endScore)
                    {
                        result.Add(item.Value.Key);
                    }
                    else
                    {
                        continue;
                    }
                }
                if (isStartScore)
                {
                    if (item.Value.Key >= startScore)
                    {
                        result.Add(item.Value.Key);
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    result.Add(item.Value.Key);
                }
            }
        }

        public void _RemoveAsync(string bytes)
        {
            var item = this.State.keyValuePairs.Where(item => item.Value.Value == bytes).FirstOrDefault();
            if (item.Key > 0)
            {
                this.State.keyValuePairs.Remove(item.Key);
            }
        }


        public Task RemoveAsync()
        {
            this.RaiseEvent(new ZSetRemove());
            return Task.CompletedTask;
        }

        public async override Task<bool> ApplyUpdatesToStorage(IReadOnlyList<ZSetExcuteBase> updates, int expectedversion)
        {
            var service = this.GrainFactory.GetGrain<IZSetStorageService>(this.GetPrimaryKeyString());
            var serviceVersion = await service.GetVersionAsync();
            if (expectedversion < serviceVersion)
            {
                return false;
            }
            return true;
        }

        public async override Task<KeyValuePair<int, ZSetState>> ReadStateFromStorage()
        {
            var service = this.GrainFactory.GetGrain<IZSetStorageService>(this.GetPrimaryKeyString());
            var state = await service.GetStateAsync();
            return new KeyValuePair<int, ZSetState>(state.Version, state);
        }

        protected async override void TransitionState(ZSetState state, ZSetExcuteBase @event)
        {
            var service = this.GrainFactory.GetGrain<IZSetStorageService>(this.GetPrimaryKeyString());
            if (@event is ZSetRemoveIndex removeIndex)
            {
                await service.RemoveByIndexAsync(removeIndex.Index);
            }
            else if (@event is ZSetRemoveScore removeScore)
            {
                await service.RemoveByScoreAsync(removeScore.Score);
            }
            else if (@event is ZSetRemoveRange removeRange)
            {
                await service.RemoveRangeAsync(removeRange.StartScore, removeRange.EndScore);
            }
            else if (@event is ZSetRemoveBytes removeBytes)
            {
                await service.RemoveAsync(removeBytes.Payload);
            }
            else if (@event is ZSet zSet)
            {
                await service.SetAsync(zSet.Payload, zSet.Score);
            }
            else
            {
                await service.RemoveAsync();
            }
        }

        protected override void NotifyStateChanged(IGrainFactory iGrainFactory)
        {
            //var service = iGrainFactory.GetGrain<IZSetStorageService>(this.GetPrimaryKeyString());
            //var state = await service.GetStateAsync();
            //this.State.keyValuePairs = state.keyValuePairs;
        }
    }

    public class ZSetExcuteBase
    {

    }

    public class ZSetRemove : ZSetExcuteBase
    {

    }


    public class ZSetRemoveIndex : ZSetExcuteBase
    {
        public int Index { get; set; }
    }
    public class ZSetRemoveScore : ZSetExcuteBase
    {
        public long Score { get; set; }
    }
    public class ZSetRemoveRange : ZSetExcuteBase
    {
        public long StartScore { get; set; }
        public long EndScore { get; set; }
    }
    public class ZSetRemoveBytes : ZSetExcuteBase
    {
        public string Payload { get; set; }
    }

    public class ZSet : ZSetExcuteBase
    {
        public long Score { get; set; }
        public string Payload { get; set; }
    }
}
